/*
 * Copyright 2009 XueSong Guo.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.webwheel;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * WebWheel MVC应用程序的主入口类，每个应用程序必须有一个类继承自这个类。<br>
 * 子类必须拥有默认无参数构造方法。
 */
abstract public class Main implements ObjectFactory {

    /**
     * 当前应用的servlet上下文。
     */
    protected ServletContext servletContext;

    /**
     * 根过滤器组。
     */
    protected FilterGroup root = new FilterGroupImpl(this);

    private Map<Class, ResultInterpreter> interpreterMap = new HashMap<Class, ResultInterpreter>();

    private static class ActionInfo {

        Class<? extends Action> actionClass;
        FilterGroupImpl filterGroup;

        private ActionInfo(Class<? extends Action> actionClass, FilterGroupImpl filterGroup) {
            this.actionClass = actionClass;
            this.filterGroup = filterGroup;
        }
    }

    private Map<String, ActionInfo> actionMap = new HashMap<String, ActionInfo>();

    /**
     * 类实例化方法。<br>
     * 框架通过此方法获得Action类的实例，可通过重载此方法来使用自定制的对象构建工厂，比如Spring等IOC容器来获得类对应的实例。<br>
     * 默认的方式是使用{@link Class#newInstance()}获得实例。
     * @param cls 需要实例化的类
     * @return 类的实例
     */
    public <T> T getInstanceOf(Class<T> cls) {
        try {
            return cls.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("can not create instance of " + cls, e);
        }
    }

    void init0(ServletContext context) throws Exception {
        this.servletContext = context;
        init();
        initFilter(root.getChildren(), new HashSet<Filter>());
        for (Map.Entry<Class, ResultInterpreter> entry : interpreterMap.entrySet()) {
            entry.getValue().init();
        }
    }

    private void initFilter(List<? extends FilterGroup> groups, Set<Filter> initedFilters) throws Exception {
        for (FilterGroup group : groups) {
            List<Filter> list = group.getFilters();
            Filter filter = list.get(list.size() - 1);
            if (!initedFilters.contains(filter)) {
                filter.init();
                initedFilters.add(filter);
            }
            initFilter(group.getChildren(), initedFilters);
        }
    }

    /**
     * 应用程序入口。<br>
     * 应在此方法的子类实现中设置动作类的绑定以及进行其他的初始化动作。<br>
     * 此方法在所有过滤器初始化之前调用。
     * @throws Exception 初始化异常
     */
    protected abstract void init() throws Exception;

    void destroy0() {
        for (Map.Entry<Class, ResultInterpreter> entry : interpreterMap.entrySet()) {
            entry.getValue().destroy();
        }
        destroyFilter(root.getChildren(), new HashSet<Filter>());
        destroy();
        servletContext = null;
        root = null;
        interpreterMap = null;
        actionMap = null;
    }

    private void destroyFilter(List<? extends FilterGroup> groups, Set<Filter> destroyedFilters) {
        for (FilterGroup group : groups) {
            destroyFilter(group.getChildren(), destroyedFilters);
            List<Filter> list = group.getFilters();
            Filter filter = list.get(list.size() - 1);
            if (!destroyedFilters.contains(filter)) {
                filter.destroy();
                destroyedFilters.add(filter);
            }
        }
    }

    /**
     * 当应用程序终止时此方法将被调用。<br>
     * 此方法在所有过滤器终止之后调用。
     */
    protected void destroy() {}

    boolean process(HttpServletRequest request, HttpServletResponse response) throws Exception {

        String path = (String) request.getAttribute("javax.servlet.include.servlet_path");
        if (path != null) {
            String info = (String) request.getAttribute("javax.servlet.include.path_info");
            if (info != null) {
                path += info;
            }
        } else {
            path = request.getServletPath();
            String info = request.getPathInfo();
            if (info != null) {
                path += info;
            }
        }
        ActionInfo ai = actionMap.get(path);
        return ai != null && new FilterChainImpl(ai.filterGroup.filters, ai.actionClass, interpreterMap,
                path, this, servletContext, request, response).go();
    }

    void bindAction(String path, Class<? extends Action> actionClass, FilterGroupImpl filterGroup) {
        if(actionClass==null) actionMap.remove(path);
        else {
            if (filterGroup == null) filterGroup = (FilterGroupImpl) root;
            actionMap.put(path, new ActionInfo(actionClass, filterGroup));
        }
    }

    /**
     * 注册动作类执行结果类型的方法。<br>
     * 当动作类返回一个结果对象时，框架将寻找对象类型所对应的解释器进行解释，寻找以从子类到父类、先类后接口的方式进行。<br>
     * 解释器实例将由{@link #getInstanceOf(Class)}创建
     * @param resultType 结果类的类型
     * @param interpreter 结果类型的解释器类型
     */
    public final <T> void registerResultType(Class<T> resultType, ResultInterpreter<? extends T> interpreter) {
        if (resultType == null) {
            throw new NullPointerException();
        }
        if(interpreter==null) interpreterMap.remove(resultType);
        else interpreterMap.put(resultType, interpreter);
    }

    /**
     * 获得当前应用的servlet上下文
     * @return servlet上下文
     */
    public ServletContext getServletContext() {
        return servletContext;
    }
}
